package devops

import (
	"fmt"
	"log"
	"net"
	"os"
	"strings"
	"time"

	"golang.org/x/crypto/ssh"
)

type SSH struct {
	client *ssh.Client
	logger *log.Logger
	envs   map[string]string
}

func (s *SSH) New(logpath string) {
	s.logger = log.Default()
	s.envs = make(map[string]string)

	f, _ := os.OpenFile(logpath, os.O_CREATE|os.O_RDWR|os.O_APPEND, os.ModeAppend|os.ModePerm)
	s.logger.SetOutput(f)
	s.logger.SetFlags(log.Ldate | log.Ltime)
}
func (s *SSH) SetEnvs(envs map[string]string) {
	for key, val := range envs {
		s.envs[key] = val
	}
}

func (s *SSH) PubKeyConnect(user, addr string, key []byte) error {
	signer, err := ssh.ParsePrivateKey(key)
	if err != nil {
		fmt.Printf("unable to parse private key: %v", err)
	}

	config := &ssh.ClientConfig{
		User: user,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(signer),
		},
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			return nil
		},
	}

	client, err := ssh.Dial("tcp", addr, config)
	if err != nil {
		fmt.Printf("unable to connect: %v", err)
		return err
	}
	s.client = client
	return nil
}

func (s *SSH) PasswdConnect(user, password, addr string) error {
	auth := make([]ssh.AuthMethod, 0)
	auth = append(auth, ssh.Password(password))

	config := &ssh.ClientConfig{
		User:    user,
		Auth:    auth,
		Timeout: 30 * time.Second,
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			return nil
		},
	}

	client, err := ssh.Dial("tcp", addr, config)
	if err != nil {
		fmt.Printf("unable to connect: %v", err)
		return err
	}
	s.client = client
	return nil
}

func (s *SSH) Close() error {
	return s.client.Close()
}

func (s *SSH) Command(args ...string) error {
	session, err := s.client.NewSession()
	if err != nil {
		fmt.Println(err.Error())
		return err
	}
	cmd := strings.Join(args, " ")
	session.Stderr = s.logger.Writer()
	session.Stdout = s.logger.Writer()

	for key, val := range s.envs {
		session.Setenv(key, val)
	}

	session.Run(cmd)

	defer session.Close()
	return nil
}

func (s *SSH) Script(arg string) (err error) {
	filename := fmt.Sprint(time.Now().Unix())

	arg = strings.ReplaceAll(arg, "\r", "")
	cmd := fmt.Sprintf("echo -e '%s'> %s.sh", arg, filename)

	err = s.Command(cmd)
	if err != nil {
		fmt.Println(err.Error())
	}
	err = s.Command(fmt.Sprintf("sh -xe ./%s.sh > %s.log 2>&1", filename, filename))
	if err != nil {
		fmt.Println(err.Error())
	}
	err = s.Command(fmt.Sprintf("cat %s.log", filename))
	if err != nil {
		fmt.Println(err.Error())
	}
	err = s.Command(fmt.Sprintf("rm -f %s.sh %s.log", filename, filename))
	if err != nil {
		fmt.Println(err.Error())
	}
	return err
}
